﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using LittleBee.Cells;
using Newtonsoft.Json.Linq;
//using System.Runtime.Remoting.Channels;
using Newtonsoft.Json;

namespace LittleBee.CellStore.UserControls
{
	public partial class UCCellPointers : UserControl
	{
		public UCCellPointers()
		{
			InitializeComponent();

			_dctAttributeControl[Pointer.Attributes.Parameter] = rbParameter;
			rbParameter.Tag = Pointer.Attributes.Parameter;
			_dctAttributeControl[Pointer.Attributes.Input] = rbInput;
			rbInput.Tag = Pointer.Attributes.Input;
			_dctAttributeControl[Pointer.Attributes.Output] = rbOutput;
			rbOutput.Tag = Pointer.Attributes.Output;
			_dctAttributeControl[Pointer.Attributes.Array] = cbArray;
			cbArray.Tag = Pointer.Attributes.Array;
			_dctAttributeControl[Pointer.Attributes.Struct] = cbList;
			cbList.Tag = Pointer.Attributes.Struct;
			_dctAttributeControl[Pointer.Attributes.Map] = cbMap;
			cbMap.Tag = Pointer.Attributes.Map;
			_dctAttributeControl[Pointer.Attributes.Polymorphic] = cbPolymorphic;
			cbMap.Tag = Pointer.Attributes.Polymorphic;
			_dctAttributeControl[Pointer.Attributes.Temporary] = cbTemporary;
			cbTemporary.Tag = Pointer.Attributes.Temporary;
			_dctAttributeControl[Pointer.Attributes.DataPacket] = cbDataPacket;
			cbDataPacket.Tag = Pointer.Attributes.DataPacket;
			_dctAttributeControl[Pointer.Attributes.ReadOnly] = cbReadOnly;
			cbReadOnly.Tag = Pointer.Attributes.ReadOnly;
			_dctAttributeControl[Pointer.Attributes.MinValue] = cbMinValue;
			cbMinValue.Tag = Pointer.Attributes.MinValue;
			_dctAttributeControl[Pointer.Attributes.MaxValue] = cbMaxValue;
			cbMaxValue.Tag = Pointer.Attributes.MaxValue;
			_dctAttributeControl[Pointer.Attributes.EngUnit] = cbEngineering;
			cbEngineering.Tag = Pointer.Attributes.EngUnit;

			cbType.DisplayMember = "Key";
			cbType.ValueMember = "Value";
			cbType.DataSource = new BindingSource(StoreCell.BaseTypes, null);
		}

		private static Dictionary<Pointer.Attributes, Control> _dctAttributeControl = new Dictionary<Pointer.Attributes, Control>();

		private Dictionary<string, TreeNode> _dctTreeNode = new Dictionary<string, TreeNode>();

		private void UCCellPointers_Load(object sender, EventArgs e)
		{
			StoreCell cell = (Tag as StoreCell)!;
			var upNode = tvPointers.Nodes;

			if (cell.DataTableCellPointers.Rows.Count == 0)
				return;

			Dictionary<string, TreeNode> dctAggregateNodes = new Dictionary<string, TreeNode>();

			foreach (DataRow item in cell.DataTableCellPointers.Rows)
			{
				string pid = item.Field<string>(Pointer.PID)!;
				string name = item.Field<string>(Pointer.NAME)!;
				Pointer.Attributes attr = (Pointer.Attributes)Enum.Parse(typeof(Pointer.Attributes), item.Field<ulong>(Pointer.ATTRIBUTE).ToString());

				var tnPointer = upNode.Add(name + "[" + pid + "]");
				tnPointer.Tag = item;

				_dctTreeNode[pid] = tnPointer;
				if ((attr & (Pointer.Attributes.Array | Pointer.Attributes.Struct)) != 0)
				{
					dctAggregateNodes[pid] = tnPointer;
				}
			}
			tvPointers.SelectedNode = tvPointers.Nodes[0];

			foreach (var item in dctAggregateNodes)
			{
				TreeNode tnItem = item.Value;
				DataRow drPointer = (tnItem.Tag as DataRow)!;

				//JObject joHelper = JObject.Parse(drPointer.Field<string>("helper"));
				JObject joVal = JObject.Parse(drPointer.Field<string>(Pointer.VALUE)!);
				if (joVal == null || !joVal.ContainsKey(Pointer.VAL))
					continue;

				Pointer.Attributes attr = (Pointer.Attributes)drPointer.Field<ulong>(Pointer.ATTRIBUTE);
				if ((attr & Pointer.Attributes.Array) != 0)
				{
					JArray jaVals = (joVal[Pointer.VAL] as JArray)!;
					if (jaVals == null)
						break;

					foreach (var jtVal in jaVals)
					{
						if (jtVal.HasValues)
						{
							tnItem.Nodes.Add(jtVal.ToString(Formatting.None));
						}
						else
						{
							JObject joVal1 = (jtVal as JObject)!;
							string strPid = joVal1[Pointer.REF]!.ToString(Formatting.None);

							TreeNode subNode = _dctTreeNode[strPid];
							DataRow drSubPointer = (subNode.Tag as DataRow)!;
							string pid = drSubPointer.Field<string>("pid")!;
							string name = drSubPointer.Field<string>("name")!;
							var tnPointer = tnItem.Nodes.Add(name + "[" + pid + "]->...");
							tnPointer.Tag = drSubPointer;
						}
					}
				}
				else if ((attr & Pointer.Attributes.Map) != 0)
				{

				}
			}
		}

		private void tvPointers_AfterSelect(object sender, TreeViewEventArgs e)
		{
			TreeNode tnItem = e.Node!;
			DataRow drItem = (tnItem.Tag as DataRow)!;

			Pointer.Attributes attr = (Pointer.Attributes)Enum.Parse(typeof(Pointer.Attributes), drItem.Field<ulong>("attribute").ToString());

			Pointer.Attributes attrPIO = (Pointer.Attributes)((ulong)Pointer.Attributes.Parameter | (ulong)Pointer.Attributes.Input | (ulong)Pointer.Attributes.Output);
			Pointer.Attributes attrTmp = (Pointer.Attributes)((ulong)attr & (ulong)attrPIO);
			if (attrTmp != Pointer.Attributes.Parameter && attrTmp != Pointer.Attributes.Input && attrTmp != Pointer.Attributes.Output)
			{
				attr = (Pointer.Attributes)((ulong)attr & (~(ulong)attrPIO) | (ulong)Pointer.Attributes.Parameter);
				drItem["attribute"] = (ulong)attr;
			}

			foreach (var item in Enum.GetValues(typeof(Pointer.Attributes)))
			{
				Pointer.Attributes attrV = (Pointer.Attributes)item;
				bool blStatus = (attrV & attr) != 0;

				if (((ulong)attrV & (ulong)attrPIO) != 0)
				{
					RadioButton control = (_dctAttributeControl[attrV] as RadioButton)!;
					control.Checked = blStatus;
				}
				else
				{
					CheckBox control = (_dctAttributeControl[attrV] as CheckBox)!;
					control.Checked = blStatus;
				}
			}
			tbPid.Text = drItem.Field<string>("pid");
			tbName.Text = drItem.Field<string>("name");
			tbDescription.Text = drItem.Field<string>("description");

			cbType.SelectedIndex = -1;
			cbType.SelectedIndex = cbType.FindStringExact(drItem.Field<string>("type"));   //drItem.Field<string>("type");
		}

		private void AttributeButton_CheckedChanged(object sender, EventArgs e)
		{
			TreeNode tnItem = tvPointers.SelectedNode;
			DataRow drItem = (tnItem.Tag as DataRow)!;

			Pointer.Attributes attr = (Pointer.Attributes)Enum.Parse(typeof(Pointer.Attributes), drItem.Field<ulong>("attribute").ToString());

			if (sender is CheckBox)
			{
				CheckBox cbAttr = (sender as CheckBox)!;
				Pointer.Attributes attrValue = (Pointer.Attributes)cbAttr.Tag;

				if (cbAttr.Checked)
					drItem["attribute"] = ((ulong)attr | (ulong)attrValue);
				else
					drItem["attribute"] = ((ulong)attr & ~(ulong)attrValue);
			}
			else
			{
				RadioButton rbAttr = (sender as RadioButton)!;
				Pointer.Attributes attrValue = (Pointer.Attributes)rbAttr.Tag;

				if (rbAttr.Checked)
					drItem["attribute"] = ((ulong)attr | (ulong)attrValue);
				else
					drItem["attribute"] = ((ulong)attr & ~(ulong)attrValue);
			}


			//drItem["attribute"] = (ulong)newAttr;
			switch (((Control)sender).Name)
			{
				case "rbParameter":

					break;
				case "rbInput":
					break;
				case "rbOutput":
					break;
				case "cbArray":
					if (cbArray.Checked == true || cbList.Checked == true)
					{
						cbMap.Checked = false;
						cbMap.Enabled = false;

						rbParameter.Checked = true;

						rbInput.Enabled = false;
						rbOutput.Enabled = false;

						cbDataPacket.Checked = false;
						cbDataPacket.Enabled = false;

						cbMaxValue.Checked = false;
						cbMaxValue.Enabled = false;

						cbMinValue.Checked = false;
						cbMinValue.Enabled = false;
					}
					else
					{
						cbMap.Enabled = true;

						rbInput.Enabled = true;

						rbOutput.Enabled = true;

						cbDataPacket.Enabled = true;

						cbMaxValue.Enabled = true;

						cbMinValue.Enabled = true;
					}

					if (cbList.Checked == true && cbArray.Checked == true)
					{
						StoreCell cell = (Tag as StoreCell)!;
						Dictionary<string, string> dctAllTypes = StoreCell.BaseTypes.Union(cell.GetStructTypes()).ToDictionary(kv => kv.Key, kv => kv.Value);

						cbType.DisplayMember = "Key";
						cbType.ValueMember = "Value";
						cbType.DataSource = new BindingSource(dctAllTypes, null);
					}
					else if (cbList.Checked == true)
					{
						StoreCell cell = (Tag as StoreCell)!;

						cbType.DataSource = new BindingSource(cell.GetStructTypes(), null);
					}
					else
					{
						cbType.DisplayMember = "Key";
						cbType.ValueMember = "Value";
						cbType.DataSource = new BindingSource(StoreCell.BaseTypes, null);
					}
					break;
				case "cbPolymorph":
					break;
				case "cbTemporary":
					break;
				case "cbReadOnly":
					break;
				case "cbDataPacket":
					break;
				case "cbMinValue":
					break;
				case "cbMaxValue":
					break;
				case "cbEngineering":
					break;
				default:
					throw new Exception("error");
			}
		}

		private void btAddPointer_Click(object sender, EventArgs e)
		{
			StoreCell cell = (Tag as StoreCell)!;

			string newPid = GetNewPid();
			DataRow drNew = cell.DataTableCellPointers.NewRow();
			drNew.BeginEdit();
			drNew["cell_id"] = cell.CellID;
			drNew["pid"] = newPid;
			drNew["name"] = newPid;
			drNew["description"] = newPid;
			drNew["attribute"] = 0x04;
			drNew["type"] = "int";
			drNew["value"] = "{}";
			drNew["helper"] = "{}";
			drNew.EndEdit();

			var upNode = tvPointers.Nodes;
			var tnPointer = upNode.Add(newPid + "[" + newPid + "]");
			tnPointer.Tag = drNew;
		}

		private string GetNewPid()
		{
			StoreCell cell = (Tag as StoreCell)!;
			int i = 1;
			string strNewPid = $"NewPid_{i:d03}";

			while (cell.DataTableCellPointers.Select($"pid = '{strNewPid}'").Length == 1)
			{
				++i;
				strNewPid = $"NewPid_{i:d03}";
			}

			return strNewPid;
		}

		private void tbPid_Leave(object sender, EventArgs e)
		{
			var currNode = tvPointers.SelectedNode;
			var drNode = (currNode.Tag as DataRow)!;

			StoreCell cell = (Tag as StoreCell)!;

			DataRow drNew = cell.DataTableCellPointers.NewRow();
			drNew.BeginEdit();
			drNew["cell_id"] = drNode.Field<Int64>("cell_id");
			drNew["pid"] = tbPid.Text;
			drNew["name"] = tbName.Text;
			drNew["description"] = tbDescription.Text;
			drNew["attribute"] = drNode.Field<UInt64>("attribute");
			drNew["type"] = drNode.Field<string>("type");
			drNew["value"] = drNode.Field<string>("value");
			drNew["helper"] = drNode.Field<string>("helper");
			drNew.EndEdit();
			cell.DataTableCellPointers.Rows.Add(drNew);

			currNode.Tag = drNew;
			drNode.Delete();

			string pid = drNew.Field<string>("pid")!;
			string name = drNew.Field<string>("name")!;
			currNode.Text = name + "[" + pid + "]";
		}

		private void tbName_Leave(object sender, EventArgs e)
		{
			var currNode = tvPointers.SelectedNode;
			var drNode = (currNode.Tag as DataRow)!;

			drNode.BeginEdit();
			drNode["name"] = tbName.Text;
			drNode.EndEdit();

			string pid = drNode.Field<string>("pid")!;
			string name = drNode.Field<string>("name")!;
			currNode.Text = name + "[" + pid + "]";
		}

		private void tbDescription_Leave(object sender, EventArgs e)
		{
			var currNode = tvPointers.SelectedNode;
			var drNode = (currNode.Tag as DataRow)!;

			drNode.BeginEdit();
			drNode["description"] = tbDescription.Text;
			drNode.EndEdit();
		}

		List<Control> _lstOptionalControls = new List<Control>();

		private void cbType_SelectedIndexChanged(object sender, EventArgs e)
		{
			var currNode = tvPointers.SelectedNode;
			if (currNode == null || cbType.SelectedIndex == -1)
				return;

			var drNode = currNode.Tag as DataRow;

			int col = 0;
			int row = 10;

			tableLayoutPanel1.SuspendLayout();

			foreach (var item in _lstOptionalControls)
			{
				tableLayoutPanel1.Controls.Remove(item);
			}
			_lstOptionalControls.Clear();

			Label lblValue = new Label();
			lblValue.Name = "lblValue";
			lblValue.Text = "缺省值：";
			lblValue.AutoSize = true;
			lblValue.Anchor = AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
			lblValue.TextAlign = ContentAlignment.MiddleRight;
			tableLayoutPanel1.Controls.Add(lblValue, col, row);
			++col;
			_lstOptionalControls.Add(lblValue);

			switch (cbType.Text)
			{
				case "bool":
					{
						Panel panel1 = new Panel();
						panel1.Dock = DockStyle.Fill;
						tableLayoutPanel1.Controls.Add(panel1, col, row);
						_lstOptionalControls.Add(panel1);

						RadioButton rbValueUnknow = new RadioButton();
						rbValueUnknow.Name = "rbValueUnknow";
						rbValueUnknow.Text = "Unknow";
						rbValueUnknow.Tag = null;
						rbValueUnknow.AutoSize = true;
						rbValueUnknow.Dock = DockStyle.Left;
						rbValueUnknow.CheckedChanged += rbValue_CheckedChanged!;
						panel1.Controls.Add(rbValueUnknow);
						_lstOptionalControls.Add(rbValueUnknow);

						RadioButton rbValueFalse = new RadioButton();
						rbValueFalse.Name = "rbValueFalse";
						rbValueFalse.Text = "false";
						rbValueFalse.Tag = false;
						rbValueFalse.AutoSize = true;
						rbValueFalse.Dock = DockStyle.Left;
						rbValueFalse.CheckedChanged += rbValue_CheckedChanged!;
						panel1.Controls.Add(rbValueFalse);
						_lstOptionalControls.Add(rbValueFalse);

						RadioButton rbValueTrue = new RadioButton();
						rbValueTrue.Name = "rbValueTrue";
						rbValueTrue.Text = "True";
						rbValueTrue.Tag = true;
						rbValueTrue.AutoSize = true;
						rbValueTrue.Dock = DockStyle.Left;
						rbValueTrue.CheckedChanged += rbValue_CheckedChanged!;
						panel1.Controls.Add(rbValueTrue);
						_lstOptionalControls.Add(rbValueTrue);

						JObject joValue = JObject.Parse(drNode![Pointer.VALUE].ToString()!);
						if (drNode["type"].ToString() == cbType.Text && joValue.ContainsKey(Pointer.VAL))
						{
							JToken joVal = joValue[Pointer.VAL]!;
							if (joVal == null || joVal.Type == JTokenType.Null)
								rbValueUnknow.Checked = true;
							else if (joVal.Value<bool>() == true)
								rbValueTrue.Checked = true;
							else
								rbValueFalse.Checked = true;
						}
						else
						{
							rbValueTrue.Checked = true;
						}
					}
					break;
				case "sbyte":
				case "short":
				case "int":
				case "long":
				case "byte":
				case "ushort":
				case "uint":
				case "ulong":
				case "decimal":
					{
						TextBox tbValue = new TextBox();
						tbValue.Name = "tbValue";
						JObject joValue = JObject.Parse(drNode![Pointer.VALUE].ToString()!);
						tbValue.Dock = DockStyle.Fill;
						tbValue.TextAlign = HorizontalAlignment.Left;
						if (drNode["type"].ToString() == cbType.Text && joValue.ContainsKey(Pointer.VAL))
						{
							if (cbType.Text == "sbyte" || cbType.Text == "short" || cbType.Text == "int" || cbType.Text == "long")
								tbValue.Text = joValue.Value<long>(Pointer.VAL).ToString();
							else if (cbType.Text == "byte" || cbType.Text == "ushort" || cbType.Text == "uint" || cbType.Text == "ulong")
								tbValue.Text = joValue.Value<ulong>(Pointer.VAL).ToString();
							else
								tbValue.Text = joValue.Value<decimal>(Pointer.VAL).ToString();
						}
						else
							tbValue.Text = "0";
						tbValue.Leave += tbValueControl_Leave!;
						tableLayoutPanel1.Controls.Add(tbValue, col, row);
						_lstOptionalControls.Add(tbValue);
					}
					break;
				case "string":
					{
						TextBox tbValue = new TextBox();
						tbValue.Name = "tbValue";
						JObject joValue = JObject.Parse(drNode![Pointer.VALUE].ToString()!);
						tbValue.Dock = DockStyle.Fill;
						tbValue.TextAlign = HorizontalAlignment.Left;
						if (drNode["type"].ToString() == cbType.Text && joValue.ContainsKey(Pointer.VAL))
							tbValue.Text = joValue.Value<string>(Pointer.VAL);
						else
							tbValue.Text = "";
						tbValue.Leave += tbValueControl_Leave!;
						tableLayoutPanel1.Controls.Add(tbValue, col, row);
						_lstOptionalControls.Add(tbValue);
					}
					break;
			}

			tableLayoutPanel1.ResumeLayout();

			drNode!.BeginEdit();
			drNode["type"] = cbType.Text;
			drNode.EndEdit();
		}
		private void rbValue_CheckedChanged(object sender, EventArgs e)
		{
			var currNode = tvPointers.SelectedNode;
			DataRow drNode = (currNode.Tag as DataRow)!;

			RadioButton rbValue = (sender as RadioButton)!;

			JObject joValue = new JObject();
			if (rbValue.Tag == null)
				joValue[Pointer.VAL] = JValue.CreateNull();
			else
				joValue[Pointer.VAL] = (bool)rbValue.Tag;

			drNode.BeginEdit();
			drNode[Pointer.VALUE] = joValue.ToString(Formatting.None);
			drNode.EndEdit();
		}

		private void tbValueControl_Leave(object sender, EventArgs e)
		{
			var currNode = tvPointers.SelectedNode;
			var drNode = (currNode.Tag as DataRow)!;

			//TextBox tbValue = Controls.Find("tbValue", true)[0] as TextBox;
			TextBox tbValue = (sender as TextBox)!;

			JObject joValue = new JObject();
			switch (cbType.Text)
			{
				case "sbyte":
				case "short":
				case "int":
				case "long":
					joValue[Pointer.VAL] = long.Parse(tbValue.Text);
					break;
				case "byte":
				case "ushort":
				case "uint":
				case "ulong":
					joValue[Pointer.VAL] = ulong.Parse(tbValue.Text);
					break;
				case "decimal":
					joValue[Pointer.VAL] = decimal.Parse(tbValue.Text);
					break;
				case "string":
					joValue[Pointer.VAL] = tbValue.Text;
					break;
			}

			drNode.BeginEdit();
			drNode[Pointer.VALUE] = joValue.ToString(Formatting.None);
			drNode.EndEdit();
		}

	}
}
